//@supresswarnings
import java.util.ArrayList;
import java.util.PriorityQueue;


public class TablaSimbolos
{
    private int numSimbolo;
    private TablaSimbolos padre;
    private ArrayList<Simbolo> simbolos;
    private static PriorityQueue<Simbolo> variables;
    private int nivel;
    private TablaTipos tablaTipos;

    /**
     * Constructor de la clase TablaSimbolos
     */
    public TablaSimbolos()
    {
        this.padre = null;
        this.simbolos = new ArrayList<Simbolo>();
        this.nivel = 0;
        if ( variables == null ) 
            this.variables = new PriorityQueue<Simbolo>();
        numSimbolo = 0;
    }


    /**
     * Constructor de la clase TablaSimbolos
     * @param padre Tabla de simbolos padre
     * @param simbolos Simbolos a añadir
     * @param nivel Nivel de la tabla
     */
    public TablaSimbolos(TablaSimbolos padre, ArrayList<Simbolo> simbolos, int nivel)
    {
        this.padre = padre;
        this.simbolos = new ArrayList<Simbolo>();
        this.simbolos = (ArrayList<Simbolo>) simbolos.clone();
        this.nivel = nivel;
    }

    public TablaSimbolos( TablaTipos tt )
    {
        tablaTipos = tt;
    }

    public TablaSimbolos (TablaSimbolos t)
    {
        padre = t.padre;
        simbolos = t.simbolos;
        tablaTipos = t.tablaTipos;
    }


    /**
     * Retirar un nivel de la tabla de simbolos. Devuelve las variables locales
     * @return
     */
    public String toString(String h)
    {
        String ret = h + ".locals (";
        boolean first = true;
        boolean escribir = true;
        if ( simbolos != null )
        { /*
            for (Simbolo s : simbolos)
            {
                if ( !first && escribir)
                    ret += ", ";
                first = false;
                if (s.tipo == Tipo.INT || s.tipo == Tipo.BOOLEAN)
                {
                    ret += "int32";
                    escribir = true;
                }
                else if (s.tipo == Tipo.DOUBLE )
                {
                    ret += "float64";
                    escribir = true;
                }
                else if ( tablaTipos.isArray(s.tipo) )
                {
                    Integer tipo = tablaTipos.getTipoBase(s.tipo);
                    if ( tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
                        ret += "int32[]";
                    else if ( tipo == Tipo.DOUBLE )
                        ret += "float64[]";
                    escribir = true;
                }
                else
                    escribir = false;
            }*/
			if (variables == null)
				variables =  new PriorityQueue<Simbolo>();
			variables.addAll(simbolos);
        }

        while ( variables!= null && !variables.isEmpty() )
        {
            Simbolo s = variables.poll();
            if ( !first && escribir)
                ret += ", ";
            first = false;
            if (s.tipo == Tipo.INT || s.tipo == Tipo.BOOLEAN)
            {
                ret += "int32";
                escribir = true;
            }
            else if (s.tipo == Tipo.DOUBLE )
            {
                ret += "float64";
                escribir = true;
            }
            else if ( tablaTipos.isArray(s.tipo) )
            {
                Integer tipo = tablaTipos.getTipoBase(s.tipo);
                if ( tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
                    ret += "int32[]";
                else if ( tipo == Tipo.DOUBLE )
                    ret += "float64[]";
                escribir = true;
            }
            else
                escribir = false;
        }

        ret += " )\n";
        return ret;
    }

    public void pop()
    {
        if ( variables == null )
            variables = new PriorityQueue<Simbolo>();

        for (Simbolo s : simbolos)
        {
            variables.offer(s);
        }

		if (padre != null)
		{
			simbolos = padre.simbolos;
			padre = padre.padre;
			nivel--;
		}
    }

    /**
     * Añade un nivel mas a la tabla de simbolos
     */

    public void push()
    {
        padre = new TablaSimbolos(this);
        simbolos = new ArrayList<Simbolo>();
        nivel++;
    }

    /**
     * Añadir un simbolo.
     * @param nombre
     * @param tipoSimbolo
     * @param tipo
     */

    public int nuevoSimbolo(String nombre, Integer tipoSimbolo, Integer tipo)
    {
        Simbolo s = new Simbolo(nombre, tipoSimbolo, tipo, numSimbolo);
        numSimbolo++;
        simbolos.add(s);
        return (numSimbolo-1);
    }

    public int crearIndice( String name )
    {
        Simbolo s = new Simbolo( name, Simbolo.INDICE, Tipo.INT,numSimbolo);
        numSimbolo++;
        simbolos.add(s);
        return (numSimbolo-1);
    }

    public int crearIndice( String name , int tipo)
    {
        Simbolo s = new Simbolo( name, Simbolo.INDICE, tipo,numSimbolo);
        numSimbolo++;
        simbolos.add(s);
        return (numSimbolo-1);
    }

/*
    public void nuevoSimbolo(String nombreMetodo, String cuerpoMetodo)
    {
        Simbolo s = new Simbolo(nombreMetodo, Simbolo.METODO, cuerpoMetodo);
        simbolos.add(s);
    }*/

    /**
     * Devolver un simbolo dado. Si no existe, se busca en el padre. Para buscar usos
     * @param nombre
     * @return
     */

    public Simbolo busca(String nombre)
    {
        if ( simbolos != null )
        {
            for (Simbolo s: simbolos)
            {
                if (s.nombre.equals(nombre))
                {
                    return s;
                }
            }
        }
    // si no lo encuentra, busca en el ámbito exterior
        if (padre != null)
            return padre.busca(nombre);

        return null; // no se encontró
    }

    public Simbolo busca(Integer id)
    {
        for (Simbolo s: simbolos)
        {
            if (s.id == id)
            {
                return s;
            }
        }
    // si no lo encuentra, busca en el ámbito exterior
        if (padre != null)
           return padre.busca(id);

        return null; // no se encontró
    }

    /**
     * Si existe una variable en un ambito. Condicion para insertar
     * @param nombre
     * @return
     */

    public boolean existe(String nombre)
    {
        for (Simbolo s: simbolos)
        {
            if (s.nombre.equals(nombre))
                return true;
        }
        return false;
    }

    /**
     * Searches for the first occurence of the given argument, testing for equality using the equals method.
     * @param nombre Nombre del Simbolo que se busca
     * @return the index of the first occurrence of the argument in this list; returns -1 if the object is not found.
     */
    public Integer getPos( String nombre )
    {
        for (Simbolo s: simbolos)
        {
            if (s.nombre.equals(nombre))
                return s.id;
        }
        if (padre != null)
            return padre.getPos(nombre);

        return -1;
    }
}

